package com.franklin.java.action.sql.run;

import com.franklin.java.api.mybatis.dom.franklin.MyMapper;
import com.franklin.java.api.mybatis.dom.utils.MapperUtils;
import com.franklin.java.utils.XmlTagUtils;
import com.franklin.plugins.common.utils.DataCheckUtils;
import com.franklin.plugins.common.utils.PatternUtils;
import com.franklin.plugins.common.utils.StringHelper;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlText;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBTextField;
import org.apache.commons.lang3.StringUtils;

import javax.swing.*;
import java.util.*;

/**
 * @author Franklin
 * @since 2021/9/22 16:51
 */
public class MybatisXmlSql extends MybatisSql<XmlTag> {

    protected MyMapper myMapper;

    protected Map<String, JComponent> elExpressionComponentMap = Collections.emptyMap();

    protected MybatisXmlSql(XmlTag sqlElement) {
        super(sqlElement);
        myMapper = new MyMapper(sqlElement);
    }

    @Override
    protected void resolveParams() {
        resolveSubTag(sqlElement);

    }

    @Override
    public String resolveSqlFromForm(Map<String, JComponent> elExpressionComponentMap) {
        this.elExpressionComponentMap = elExpressionComponentMap;
        return resolveSql(this.sqlElement,this.myMapper);
    }

    @Override
    public String getMapperMethodName() {
        return this.sqlElement.getAttributeValue("id");
    }

    private String resolveChildSql(List<XmlElement> xmlElementList, MyMapper myMapper) {
        StringBuilder stringBuilder = new StringBuilder();
        for (XmlElement xmlElement : xmlElementList) {
            if (xmlElement instanceof XmlText) {
                XmlText xmlText = (XmlText) xmlElement;
                String xmlTextValue = xmlText.getValue();
                stringBuilder.append(xmlTextValue);
            } else {
                XmlTag xmlTag = (XmlTag) xmlElement;
                stringBuilder.append(resolveSql(xmlTag,myMapper));
            }
        }
        return stringBuilder.toString().trim();
    }

    private String resolveSql(XmlTag xmlTag,MyMapper myMapper) {
        String tagName = xmlTag.getName();
        StringBuilder stringBuilder = new StringBuilder();
        List<XmlElement> xmlElementList = XmlTagUtils.findXmlChild(xmlTag);
        String childValue = resolveChildSql(xmlElementList,myMapper);
        if (tagName.equals("if")) {
            String test = xmlTag.getAttributeValue("test");
            JComponent component = this.elExpressionComponentMap.get(test);
            if (Objects.nonNull(component)) {
                JBCheckBox jbCheckBox = (JBCheckBox) component;
                if (jbCheckBox.isSelected()) {
                    stringBuilder.append(" ").append(childValue);
                }
            }
        } else if (tagName.equals("choose")) {
            StringBuilder chooseBuilder = new StringBuilder();
            boolean choose = false;
            for (XmlElement xmlElement : xmlElementList) {
                if (xmlElement instanceof XmlText) {
                    XmlText xmlText = (XmlText) xmlElement;
                    String xmlTextValue = xmlText.getValue();
                    chooseBuilder.append(xmlTextValue);
                } else {
                    XmlTag chooseChildTag = (XmlTag) xmlElement;
                    String chooseChildTagName = chooseChildTag.getName();
                    if (chooseChildTagName.equals("when")){
                        String test = chooseChildTag.getAttributeValue("test");
                        JComponent component = this.elExpressionComponentMap.get(test);
                        if (Objects.nonNull(component)) {
                            JBCheckBox jbCheckBox = (JBCheckBox) component;
                            if (jbCheckBox.isSelected()) {
                                chooseBuilder.append(" ").append(resolveSql(chooseChildTag,myMapper));
                                choose = true;
                            }
                        }
                    }else if (chooseChildTagName.equals("otherwise")){
                        if (!choose){
                            chooseBuilder.append(" ").append(resolveSql(chooseChildTag,myMapper));
                        }
                    }else {
                        chooseBuilder.append(" ").append(resolveSql(chooseChildTag,myMapper));
                    }
                }
            }
            stringBuilder.append(" ").append(chooseBuilder);
        } else if (tagName.equals("where")) {
            if (DataCheckUtils.isNotEmpty(childValue)) {
                StringBuilder whereFirstKeywordBuilder = new StringBuilder();
                for (int i = 0; i < childValue.length(); i++) {
                    char c = childValue.charAt(i);
                    if (c == ' ') {
                        break;
                    }
                    whereFirstKeywordBuilder.append(c);
                }
                childValue = childValue.replaceFirst(whereFirstKeywordBuilder.toString(), "");
            }
            if (StringUtils.isNotBlank(childValue)) {
                stringBuilder.append("where").append(' ').append(childValue);
            }
        } else if (tagName.equals("include")) {
            String refid = Optional
                .ofNullable(xmlTag.getAttribute("refid"))
                .map(XmlAttribute::getValue).orElse("");
            if (refid.contains(".")) {
                int lastDotIndex = refid.lastIndexOf(".");
                String refMapper = refid.substring(0, lastDotIndex);
                String refSqlId = refid.substring(lastDotIndex + 1);
                String refSql = findRefSql(
                    xmlTag.getContainingFile().getVirtualFile().getPath(),
                    refMapper,
                    refSqlId
                );
                childValue = refSql;
            } else {
                //当前Mapper的SQL引用
                XmlTag sqlTag = myMapper.getSql(refid);
                String subSql = Optional.ofNullable(sqlTag)
                    .map(tag -> this.resolveSql(tag,myMapper))
                    .orElse("");
                childValue = subSql;
            }

            stringBuilder.append(" ").append(childValue);
        } else if (tagName.equals("foreach")) {
            String open = Optional
                .ofNullable(xmlTag.getAttribute("open"))
                .map(XmlAttribute::getValue).orElse("");

            String close = Optional
                .ofNullable(xmlTag.getAttribute("close"))
                .map(XmlAttribute::getValue).orElse("");

            String item = Optional
                .ofNullable(xmlTag.getAttribute("item"))
                .map(XmlAttribute::getValue).orElse("");

            String index = Optional
                .ofNullable(xmlTag.getAttribute("index"))
                .map(XmlAttribute::getValue).orElse("");

            if (DataCheckUtils.isNotEmpty(item)) {
                String elExpression = PatternUtils.findElExpression(childValue, item);
                if (DataCheckUtils.isNotEmpty(elExpression)) {
                    childValue = fillElValue(childValue, elExpression);
                }
            }

            if (DataCheckUtils.isNotEmpty(index)) {
                String elExpression = PatternUtils.findElExpression(childValue, index);
                if (DataCheckUtils.isNotEmpty(elExpression)) {
                    childValue = fillElValue(childValue, elExpression);
                }
            }

            StringBuilder foreachBuilder = new StringBuilder();
            foreachBuilder.append(open);
            foreachBuilder.append(childValue);
            foreachBuilder.append(close);

            stringBuilder.append(foreachBuilder);
        } else {
            for (Map.Entry<String, JComponent> entry : this.elExpressionComponentMap.entrySet()) {
                JComponent jComponent = entry.getValue();
                if (jComponent instanceof JBTextField){
                    childValue = fillElValue(childValue,entry.getKey());
                }
            }
            stringBuilder.append(" ").append(childValue);
        }
        return stringBuilder.toString();
    }

    /**
     * 填值<br>
     * @author Franklin
     * @since 2021/9/23 12:14
     * @param childValue:
     * @param elExpression:
     * @return java.lang.String
     */
    private String fillElValue(String childValue, String elExpression) {
        JComponent component = this.elExpressionComponentMap.get(elExpression);
        JBTextField jbTextField = (JBTextField) component;
        String fillValue = jbTextField.getText();
        childValue = StringHelper.fillElExpression(childValue, elExpression, fillValue);
        return childValue;
    }

    private String findRefSql(String path, String refMapper, String refSqlId) {
        MyMapper mapper = MapperUtils.findMapper(sqlElement.getProject(), refMapper, path);
        return Optional.ofNullable(mapper)
            .map(myMapper -> myMapper.getSql(refSqlId))
            .map(sql -> resolveSql(sql,mapper))
            .orElse("");
    }


    private void resolveSubTag(XmlTag xmlTag) {
        String tagName = xmlTag.getName();
        if (tagName.equals("if") || tagName.equals("when")) {
            String test = xmlTag.getAttributeValue("test");
            this.paramList.add(new MybatisParam(test, false, true));
        }

        List<XmlElement> xmlChildren = XmlTagUtils.findXmlChild(xmlTag);
        resolveChildTag(xmlChildren);
    }

    private void resolveChildTag(List<XmlElement> xmlChildren) {
        for (XmlElement xmlChild : xmlChildren) {
            if (xmlChild instanceof XmlText) {
                XmlText xmlText = (XmlText) xmlChild;
                String xmlTextValue = xmlText.getValue();
                List<String> elExpressions = StringHelper.findElExpression(xmlTextValue);
                for (String elExpression : elExpressions) {
                    boolean isString = elExpression.toLowerCase().contains("char");
                    this.paramList.add(new MybatisParam(elExpression, isString));
                }
            } else {
                XmlTag xmlTag = (XmlTag) xmlChild;
                resolveSubTag(xmlTag);
            }
        }
    }

}
